package io.gitee.chearnee.fw.datasecurity.mybatisplus.inspect;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.gitee.chearnee.fw.datasecurity.core.executor.DefaultCryptExecutor;
import io.gitee.chearnee.fw.datasecurity.core.handler.DefaultAESCodecFieldValueHandler;
import io.gitee.chearnee.fw.datasecurity.core.properties.DataSecurityDbCryptProperty;
import io.gitee.chearnee.fw.datasecurity.mybatisplus.interceptor.MybatisPlusCryptInterceptor;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.annotation.Order;

import java.util.Map;

/**
 * @author kam
 * @since 2021/10/14
 *
 * <p>
 * mybatis插件监察监听器
 * </p>
 */
@Order(-2)
@Slf4j
public class MybatisInterceptorInspect {


    private final SqlSessionFactory sessionFactory;
    private final DataSecurityDbCryptProperty dbCryptProperty;

    public MybatisInterceptorInspect(SqlSessionFactory sessionFactory, DataSecurityDbCryptProperty dbCryptProperty) {
        this.sessionFactory = sessionFactory;
        this.dbCryptProperty = dbCryptProperty;
    }

    @PostConstruct
    public void initPageInterceptor() {
        Configuration configuration = sessionFactory.getConfiguration();
        if (configuration instanceof MybatisConfiguration mybatisConfiguration) {
            MybatisPlusCryptInterceptor interceptor = new MybatisPlusCryptInterceptor(dbCryptProperty,
                    new DefaultCryptExecutor(new DefaultAESCodecFieldValueHandler(dbCryptProperty)));
            //注册
            mybatisConfiguration.addInterceptor(interceptor);
        }
    }

    /**
     * 从MetaObject获取Page对象
     *
     * @param metaObject 元数据参数对象
     * @return 分页对象
     */
    private IPage<?> obtainPageParam(MetaObject metaObject) {
        //以下代码摘自：PaginationInterceptor 主要是拿到page对象
        //========================================================================

        // 先判断是不是SELECT操作  (2019-04-10 00:37:31 跳过存储过程)
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType()
                || StatementType.CALLABLE == mappedStatement.getStatementType()) {
            return null;
        }

        // 针对定义了rowBounds，做为mapper接口方法的参数
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        Object paramObj = boundSql.getParameterObject();

        // 判断参数里是否有page对象
        IPage<?> page = null;
        if (paramObj instanceof IPage) {
            page = (IPage<?>) paramObj;
        } else if (paramObj instanceof Map) {
            for (Object arg : ((Map<?, ?>) paramObj).values()) {
                if (arg instanceof IPage) {
                    page = (IPage<?>) arg;
                    break;
                }
            }
        }
        return page;
    }
}
